use crate::vul::{Normal, Vertex};
use std::ops::Sub;
// counter clock-wise 4 point. from lower left.
fn construct_square(
    a: (f32, f32, f32),
    b: (f32, f32, f32),
    c: (f32, f32, f32),
    d: (f32, f32, f32),
    color: [f32; 4],
) -> (Vec<Vertex>, Vec<Normal>, Vec<u32>) {
    // give a clockwise order vertex
    let vertex = vec![a, b, c, d];
    let local_vertex: Vec<Vertex> = vertex
        .iter()
        .filter_map(|(x, y, z)| {
            Some(Vertex {
                position: [*x, *y, *z],
                color, // TODO color generator
            })
        })
        .collect();
    let mut local_normal = triangle_normal(&local_vertex[0], &local_vertex[1], &local_vertex[2]);
    local_normal.append(&mut triangle_normal(
        &local_vertex[1],
        &local_vertex[2],
        &local_vertex[3],
    ));
    let local_index = vec![1, 2, 3, 3, 4, 1u32];
    (local_vertex, local_normal, local_index)
}

fn construct_arc_face(
    a: [f32; 3],
    b: [f32; 3],
    c: [f32; 3],
    d: [f32; 3],
    center: [f32; 3],
    color: [f32; 4],
) -> (Vec<Vertex>, Vec<Normal>, Vec<u32>) {
    let local_point = vec![a, b, c, d];
    let local_vertex = local_point
        .iter()
        .map(|&position| Vertex { position, color })
        .collect();
    let local_index = [1, 2, 3, 3, 4, 1u32].to_vec();
    let local_normal = local_index
        .iter()
        .map(|&x| Normal {
            normal: vec_sub(local_point[x as usize - 1], center),
        })
        .collect();
    (local_vertex, local_normal, local_index)
}

// counter clock-wise 3 point. from lower left.
pub fn triangle_normal(a: &Vertex, b: &Vertex, c: &Vertex) -> Vec<Normal> {
    vec![
        normal_cross(*b - *a, *c - *a),
        normal_cross(*c - *b, *a - *b),
        normal_cross(*a - *c, *b - *c),
    ]
}

fn vec_sub(a: [f32; 3], b: [f32; 3]) -> [f32; 3] {
    [a[0] - b[0], a[1] - b[1], a[2] - b[2]]
}

fn normal_cross(lhs: Normal, rhs: Normal) -> Normal {
    let [a0, a1, a2] = lhs.normal;
    let [b0, b1, b2] = rhs.normal;
    Normal {
        normal: [a1 * b2 - a2 * b1, a2 * b0 - a0 * b2, a0 * b1 - a1 * b0],
    }
}
//  _            _                   _
// (_)_ __  _ __| |___ _ __  ___ _ _| |_ ___
// | | '  \| '_ \ / -_) '  \/ -_) ' \  _(_-<
// |_|_|_|_| .__/_\___|_|_|_\___|_||_\__/__/
//         |_|

impl Sub for Vertex {
    type Output = Normal;
    fn sub(self, rhs: Self) -> Self::Output {
        Self::Output {
            normal: [
                self.position[0] - rhs.position[0],
                self.position[1] - rhs.position[1],
                self.position[2] - rhs.position[2],
            ],
        }
    }
}
impl Vertex {
    pub fn zero() -> Self {
        Self {
            color: [0., 0., 0., 0.],
            position: [0., 0., 0.],
        }
    }
}

impl From<[f32; 3]> for Normal {
    fn from(n: [f32; 3]) -> Self {
        Normal { normal: n }
    }
}

impl Normal {
    pub fn zero() -> Self {
        Normal {
            normal: [0., 0., 0.],
        }
    }
}

#[test]
fn test_square_ctor() {
    let res = construct_square(
        (0.0, 0.0, 0.0),
        (1.0, 0.0, 0.0),
        (1.0, 1.0, 0.0),
        (0.0, 1.0, 0.0),
        [1.0, 1.0, 1.0, 1.0],
    );
    let vert_test = vec![
        Vertex {
            position: [0.0, 0.0, 0.0],
            color: [1.0, 1.0, 1.0, 1.0],
        },
        Vertex {
            position: [1.0, 0.0, 0.0],
            color: [1.0, 1.0, 1.0, 1.0],
        },
        Vertex {
            position: [1.0, 1.0, 0.0],
            color: [1.0, 1.0, 1.0, 1.0],
        },
        Vertex {
            position: [0.0, 1.0, 0.0],
            color: [1.0, 1.0, 1.0, 1.0],
        },
    ];
    let norm_test = vec![
        Normal {
            normal: [0.0, 0.0, 1.0],
        },
        Normal {
            normal: [0.0, -0.0, 1.0],
        },
        Normal {
            normal: [0.0, 0.0, 1.0],
        },
        Normal {
            normal: [0.0, -0.0, 1.0],
        },
        Normal {
            normal: [0.0, 0.0, 1.0],
        },
        Normal {
            normal: [-0.0, 0.0, 1.0],
        },
    ];
    let inde_test = vec![1, 2, 3, 3, 4, 1u32];
    // assert_eq!(res.0, vert_test);
    // assert_eq!(res.1, norm_test);
    // assert_eq!(res.2, inde_test);
}
